Assignment Objectives

  • Develop a clear technical understanding of nonparametric cumulative distribution function (CDF) estimation and various kernel density estimators.

  • Translate mathematical formulas into R functions and apply them to solve related problems.

  • Create effective visualizations to demonstrate your understanding of key concepts in the following questions.


Question 1: Cumulative Distribution Function (CDF) Estimation

The following failure times (in hours) were observed for 8 electronic components:

23, 45, 67, 89, 112, 156, 189, 245
  1. Write an R function implementing the ECDF \(\hat{F}_n(t)\) according to its mathematical definition. Validate your implementation using R’s ecdf() function on the given data, with comparison based on their step functions.

We are given the following definition for \(\hat{F}_n(t)\)

\[ \hat{F}_n(t) = \frac{1}{n} \sum_{i=1}^{n} \mathbb{I} \left(t_n < t \right)\] where,

\[ \mathbb{I}(t) = \begin{cases} 0 & t_n > t\\ 1 & t_n \leq t \end{cases} \]

random_sample <- c(23, 45, 67, 89, 112, 156, 189, 245)


sams_ecdf <- function(x) {
  #sort in ascending order
  #sample <- sort(sample, decreasing=FALSE)
  n <- length(x)
  function (t) {
    mat <- outer(x, t, FUN="<=")
    return (colSums(mat) / n)
  }
}

#some tests to check my function behaves correctly
r_lang_ecdf <- stats::ecdf(random_sample)
sam_ecdf <- sams_ecdf(random_sample)

test_points <- seq(20, 250, 1)
results <- r_lang_ecdf(test_points) == sam_ecdf(test_points)
print(paste("fails: ", length(results[!results])))
[1] "fails:  0"
print(paste("passes: ", length(results[results])))
[1] "passes:  231"
x_points <- seq(20, 250, 0.5)
plot <- ggplot(data=data.frame(x=x_points, y_1=sam_ecdf(x_points), y_2=r_lang_ecdf(x_points))) +
  geom_point(aes(x=x, y=y_1, color="Sam's ECDF")) + 
  geom_point(aes(x=x, y=y_2, color="R's ECDF"))

ggplotly(plot)
  1. A colleague claims that the probability of failure before 100 hours is 0.5 based on these data. Do you agree? Explain your reasoning using the empirical cumulative distribution function (ECDF).
random_sample <- c(23, 45, 67, 89, 112, 156, 189, 245)
r_ecdf <- stats::ecdf(random_sample)
print(r_ecdf(100))
[1] 0.5

Here we compute ECDF(100 hours). This is the porportion of data values that are less than or equal to 100. This value approximates the true CDF, the probability that a failure occurs at or before 100 hours. From the given data we get that ECDF(100) = 0.5, which is what the a colleague claims is the probability of failure. I agree that from these data, 0.5 is a reasonable estimation for the probability of failure before 100 hours.

Question 2: Density Function Estimation

Consider the following failure times from a mechanical system:

12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4
  1. Create a histogram of the data using 3 equally spaced bins. What is the estimated density in each bin? Describe the shape of the histogram’s distribution.
random_sample <- c(12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4)
d_frame <- data.frame(x=random_sample)
plot <- ggplot(data=d_frame, aes(x = x)) +
  geom_histogram(aes(y = after_stat(density)),bins=3)
ggplotly(plot)
  1. Write an R function that computes kernel density estimates using a Gaussian kernel with \(h=2\). Validate your implementation against R’s built-in density() function.

\[ \hat{f}_h(t) = \frac{1}{nh}\sum_{i=1}^n K\left( \frac{t-t_i}{h}\right), \ \ \text{ where } \ \ K(u) = \frac{1}{\sqrt{2\pi}} e^{-u^2/2}. \]

kernel_density <- function(sample, K=dnorm, h=2) {
  n <- length(sample)
  function(t, h=2) {
    k <- K( outer(t, sample, FUN="-") / h)
    s <- rowSums(k)
    return (s / n / h)
  }
}

#x <- rnorm(100)
#x <- rgamma(100, 2)
x <- c(12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4)
#dat <- data.frame(x=x, y=K(x))
#ggplot(dat) +
#  geom_point(aes(x, y)) +
#  geom_line(data=density(x), aes(x=x,y=y))
plot(density(x, kernel="gaussian", bw=2))
my_gaussian_kernel_density <- kernel_density(x)
#print(my_gaussian_kernel_density(-10:10:0.1))
points(x=seq(-10, 50, 0.1), y=my_gaussian_kernel_density(seq(-10, 50, 0.1)))

c) Write a custom R function that computes kernel density estimates using the Epanechnikov kernel with \(h=2\). Validate your implementation by comparing results with R’s built-in density() function for Gaussian kernel estimation.

\[ \hat{f}_h(t) = \frac{1}{nh}\sum_{i=1}^n K\left( \frac{t-t_i}{h}\right), \ \ \text{ where } \ \ K(u) = \frac{3}{4}(1 - u^2) \ \ \text{ for } \ \ |u| \le 1. \]

x <- c(12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4)

K <- function(u) {
    mask <- abs(u) <= 1
    return (((3/4) * (1 - u^2)) * mask)
}

kernel_density <- function(sample, K=dnorm) {
  function(t, h=2) {
    n <- length(sample)
    #compute a matrix where each row are the
    #terms to sum for each element of t
    k <- K(outer(t, sample, FUN="-") / h)
    #sum for each value of t
    s <- rowSums(k)
    #scale and return
    return (s / h / n)
  }
}

my_epanechnikov_kernel_density <- kernel_density(x, K=K)
x_axis <- seq(5, 30, 0.01)


plot(density(x, kernel="gaussian", bw=2), main="Gaussian and epanechnikov kernels")
points(x=x_axis, y=my_epanechnikov_kernel_density(x_axis), pch = 20, cex = 0.1)

  1. How does the choice of kernel (Gaussian vs. Epanechnikov) affect the density estimate? For both kernel estimators applied to this dataset, what happens when we select \(h=1.5\) versus \(h=2.5\)?
x <- c(12.3, 14.7, 15.2, 16.8, 18.1, 19.4, 20.6, 22.3, 23.9, 25.4)
plot <- ggplot(data=data.frame(x=x), aes(x=x)) +
  geom_density(bw=1.5, kernel="gaussian", aes(color="gausian h=1.5")) +
  geom_density(bw=1.5, kernel="epanechnikov", aes(color="epanechnikov h=1.5")) +
  geom_density(bw=2.5, kernel="gaussian", aes(color="gausian h=2.5")) +
  geom_density(bw=2.5, kernel="epanechnikov", aes(color="epanechnikov h=2.5")) +
  labs(title="Impact of Binwidth and Kernel on Density Approximation")

ggplotly(plot)
LS0tCnRpdGxlOiAiQXNzaWdubWVudCAxOiBFc3RpbWF0aW5nIENERiBhbmQgUERGIgphdXRob3I6ICJTYW11ZWwgSm9obnNvbiIKaGVhZGVyLWluY2x1ZGVzOgogIC0gXHVzZXBhY2thZ2V7YW1zc3ltYn0KZGF0ZTogIiBEdWU6IDIvMy8yMDI2IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICB0b2NfZmxvYXQ6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiBubwogICAgdG9jX2NvbGxhcHNlZDogeWVzCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgc21vb3RoX3Njcm9sbDogeWVzCiAgICB0aGVtZTogbHVtZW4KICBwZGZfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIGZpZ193aWR0aDogMwogICAgZmlnX2hlaWdodDogMwogIHdvcmRfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAga2VlcF9tZDogeWVzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge2NzcywgZWNobyA9IEZBTFNFfQojVE9DOjpiZWZvcmUgewogIGNvbnRlbnQ6ICJUYWJsZSBvZiBDb250ZW50cyI7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgZm9udC1zaXplOiAxLjJlbTsKICBkaXNwbGF5OiBibG9jazsKICBjb2xvcjogbmF2eTsKICBtYXJnaW4tYm90dG9tOiAxMHB4Owp9CgoKZGl2I1RPQyBsaSB7ICAgICAvKiB0YWJsZSBvZiBjb250ZW50ICAqLwogICAgbGlzdC1zdHlsZTp1cHBlci1yb21hbjsKICAgIGJhY2tncm91bmQtaW1hZ2U6bm9uZTsKICAgIGJhY2tncm91bmQtcmVwZWF0Om5vbmU7CiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7Cn0KCmgxLnRpdGxlIHsgICAgLyogbGV2ZWwgMSBoZWFkZXIgb2YgdGl0bGUgICovCiAgZm9udC1zaXplOiAyMnB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7Cn0KCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovCiAgZm9udC1zaXplOiAxNXB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7CiAgY29sb3I6IG5hdnk7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CgpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOwogIGNvbG9yOiBEYXJrQmx1ZTsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KCmgxIHsgLyogSGVhZGVyIDEgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMjBweDsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGNlbnRlcjsKfQoKaDIgeyAvKiBIZWFkZXIgMiAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKaDMgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogICAgZm9udC1zaXplOiAxNnB4OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKaDQgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogICAgZm9udC1zaXplOiAxNHB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCi8qIEFkZCBkb3RzIGFmdGVyIG51bWJlcmVkIGhlYWRlcnMgKi8KLmhlYWRlci1zZWN0aW9uLW51bWJlcjo6YWZ0ZXIgewogIGNvbnRlbnQ6ICIuIjsKCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9CgouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQoKcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0KCn0KYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IAojIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dCBmaWxlcy4KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikKICAgbGlicmFyeShrbml0cikKfQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpCiAgIGxpYnJhcnkocGFuZGVyKQp9CmlmICghcmVxdWlyZSgiZ2dwbG90MiIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCiAgbGlicmFyeShnZ3Bsb3QyKQp9CmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQogIGxpYnJhcnkodGlkeXZlcnNlKQp9CgppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikKICBsaWJyYXJ5KHBsb3RseSkKfQojIyMjCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsICAgIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIAogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsICAgICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLgogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BCiAgICAgICAgICAgICAgICAgICAgICApICAKYGBgCiAKIFwKIAojIyAqKkFzc2lnbm1lbnQgT2JqZWN0aXZlcyoqIAoKKiBEZXZlbG9wIGEgY2xlYXIgdGVjaG5pY2FsIHVuZGVyc3RhbmRpbmcgb2Ygbm9ucGFyYW1ldHJpYyBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbiAoQ0RGKSBlc3RpbWF0aW9uIGFuZCB2YXJpb3VzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRvcnMuCgoqIFRyYW5zbGF0ZSBtYXRoZW1hdGljYWwgZm9ybXVsYXMgaW50byBSIGZ1bmN0aW9ucyBhbmQgYXBwbHkgdGhlbSB0byBzb2x2ZSByZWxhdGVkIHByb2JsZW1zLgoKKiBDcmVhdGUgZWZmZWN0aXZlIHZpc3VhbGl6YXRpb25zIHRvIGRlbW9uc3RyYXRlIHlvdXIgdW5kZXJzdGFuZGluZyBvZiBrZXkgY29uY2VwdHMgaW4gdGhlIGZvbGxvd2luZyBxdWVzdGlvbnMuCgoKClwKCiMjICoqUXVlc3Rpb24gMTogQ3VtdWxhdGl2ZSBEaXN0cmlidXRpb24gRnVuY3Rpb24gKENERikgRXN0aW1hdGlvbioqCgpUaGUgZm9sbG93aW5nIGZhaWx1cmUgdGltZXMgKGluIGhvdXJzKSB3ZXJlIG9ic2VydmVkIGZvciA4IGVsZWN0cm9uaWMgY29tcG9uZW50czoKCjxjZW50ZXI+IDIzLCA0NSwgNjcsIDg5LCAxMTIsIDE1NiwgMTg5LCAyNDUgIDwvY2VudGVyPgoKYSkgV3JpdGUgYW4gUiBmdW5jdGlvbiBpbXBsZW1lbnRpbmcgdGhlIEVDREYgJFxoYXR7Rn1fbih0KSQgYWNjb3JkaW5nIHRvIGl0cyBtYXRoZW1hdGljYWwgZGVmaW5pdGlvbi4gVmFsaWRhdGUgeW91ciBpbXBsZW1lbnRhdGlvbiB1c2luZyBSJ3MgZWNkZigpIGZ1bmN0aW9uIG9uIHRoZSBnaXZlbiBkYXRhLCB3aXRoIGNvbXBhcmlzb24gYmFzZWQgb24gdGhlaXIgc3RlcCBmdW5jdGlvbnMuCgpXZSBhcmUgZ2l2ZW4gdGhlIGZvbGxvd2luZyBkZWZpbml0aW9uIGZvciAkXGhhdHtGfV9uKHQpJAoKJCQgXGhhdHtGfV9uKHQpID0gXGZyYWN7MX17bn0gXHN1bV97aT0xfV57bn0gXG1hdGhiYntJfSBcbGVmdCh0X24gPCB0IFxyaWdodCkkJAp3aGVyZSwKCiQkIFxtYXRoYmJ7SX0odCkgPSAKICBcYmVnaW57Y2FzZXN9CiAgICAgIDAgJiAgdF9uID4gdFxcCiAgICAgIDEgJiB0X24gXGxlcSB0CiAgXGVuZHtjYXNlc30gCiQkCmBgYHtyfQpyYW5kb21fc2FtcGxlIDwtIGMoMjMsIDQ1LCA2NywgODksIDExMiwgMTU2LCAxODksIDI0NSkKCgpzYW1zX2VjZGYgPC0gZnVuY3Rpb24oeCkgewogICNzb3J0IGluIGFzY2VuZGluZyBvcmRlcgogICNzYW1wbGUgPC0gc29ydChzYW1wbGUsIGRlY3JlYXNpbmc9RkFMU0UpCiAgbiA8LSBsZW5ndGgoeCkKICBmdW5jdGlvbiAodCkgewogICAgbWF0IDwtIG91dGVyKHgsIHQsIEZVTj0iPD0iKQogICAgcmV0dXJuIChjb2xTdW1zKG1hdCkgLyBuKQogIH0KfQoKI3NvbWUgdGVzdHMgdG8gY2hlY2sgbXkgZnVuY3Rpb24gYmVoYXZlcyBjb3JyZWN0bHkKcl9sYW5nX2VjZGYgPC0gc3RhdHM6OmVjZGYocmFuZG9tX3NhbXBsZSkKc2FtX2VjZGYgPC0gc2Ftc19lY2RmKHJhbmRvbV9zYW1wbGUpCgp0ZXN0X3BvaW50cyA8LSBzZXEoMjAsIDI1MCwgMSkKcmVzdWx0cyA8LSByX2xhbmdfZWNkZih0ZXN0X3BvaW50cykgPT0gc2FtX2VjZGYodGVzdF9wb2ludHMpCnByaW50KHBhc3RlKCJmYWlsczogIiwgbGVuZ3RoKHJlc3VsdHNbIXJlc3VsdHNdKSkpCnByaW50KHBhc3RlKCJwYXNzZXM6ICIsIGxlbmd0aChyZXN1bHRzW3Jlc3VsdHNdKSkpCmBgYApgYGB7cn0KeF9wb2ludHMgPC0gc2VxKDIwLCAyNTAsIDAuNSkKcGxvdCA8LSBnZ3Bsb3QoZGF0YT1kYXRhLmZyYW1lKHg9eF9wb2ludHMsIHlfMT1zYW1fZWNkZih4X3BvaW50cyksIHlfMj1yX2xhbmdfZWNkZih4X3BvaW50cykpKSArCiAgZ2VvbV9wb2ludChhZXMoeD14LCB5PXlfMSwgY29sb3I9IlNhbSdzIEVDREYiKSkgKyAKICBnZW9tX3BvaW50KGFlcyh4PXgsIHk9eV8yLCBjb2xvcj0iUidzIEVDREYiKSkKCmdncGxvdGx5KHBsb3QpCmBgYAoKCmIpIEEgY29sbGVhZ3VlIGNsYWltcyB0aGF0IHRoZSBwcm9iYWJpbGl0eSBvZiBmYWlsdXJlIGJlZm9yZSAxMDAgaG91cnMgaXMgMC41IGJhc2VkIG9uIHRoZXNlIGRhdGEuIERvIHlvdSBhZ3JlZT8gRXhwbGFpbiB5b3VyIHJlYXNvbmluZyB1c2luZyB0aGUgZW1waXJpY2FsIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIChFQ0RGKS4KCmBgYHtyfQpyYW5kb21fc2FtcGxlIDwtIGMoMjMsIDQ1LCA2NywgODksIDExMiwgMTU2LCAxODksIDI0NSkKcl9lY2RmIDwtIHN0YXRzOjplY2RmKHJhbmRvbV9zYW1wbGUpCnByaW50KHJfZWNkZigxMDApKQpgYGAKSGVyZSB3ZSBjb21wdXRlIEVDREYoMTAwIGhvdXJzKS4gVGhpcyBpcyB0aGUgcG9ycG9ydGlvbiBvZiBkYXRhIHZhbHVlcyB0aGF0IGFyZSBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gMTAwLiBUaGlzIHZhbHVlIGFwcHJveGltYXRlcyB0aGUgdHJ1ZSBDREYsIHRoZSBwcm9iYWJpbGl0eSB0aGF0IGEgZmFpbHVyZSBvY2N1cnMgYXQgb3IgYmVmb3JlIDEwMCBob3Vycy4gRnJvbSB0aGUgZ2l2ZW4gZGF0YSB3ZSBnZXQgdGhhdCBFQ0RGKDEwMCkgPSAwLjUsIHdoaWNoIGlzIHdoYXQgdGhlIGEgY29sbGVhZ3VlIGNsYWltcyBpcyB0aGUgcHJvYmFiaWxpdHkgb2YgZmFpbHVyZS4gSSBhZ3JlZSB0aGF0IGZyb20gdGhlc2UgZGF0YSwgMC41IGlzIGEgcmVhc29uYWJsZSBlc3RpbWF0aW9uIGZvciB0aGUgcHJvYmFiaWxpdHkgb2YgZmFpbHVyZSBiZWZvcmUgMTAwIGhvdXJzLgoKCiMjICoqUXVlc3Rpb24gMjogRGVuc2l0eSBGdW5jdGlvbiBFc3RpbWF0aW9uKioKCkNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgZmFpbHVyZSB0aW1lcyBmcm9tIGEgbWVjaGFuaWNhbCBzeXN0ZW06Cgo8Y2VudGVyPiAxMi4zLCAxNC43LCAxNS4yLCAxNi44LCAxOC4xLCAxOS40LCAyMC42LCAyMi4zLCAyMy45LCAyNS40IDwvY2VudGVyPgoKYSkgQ3JlYXRlIGEgaGlzdG9ncmFtIG9mIHRoZSBkYXRhIHVzaW5nIDMgZXF1YWxseSBzcGFjZWQgYmlucy4gV2hhdCBpcyB0aGUgZXN0aW1hdGVkIGRlbnNpdHkgaW4gZWFjaCBiaW4/IERlc2NyaWJlIHRoZSBzaGFwZSBvZiB0aGUgaGlzdG9ncmFtJ3MgZGlzdHJpYnV0aW9uLgoKYGBge3J9CnJhbmRvbV9zYW1wbGUgPC0gYygxMi4zLCAxNC43LCAxNS4yLCAxNi44LCAxOC4xLCAxOS40LCAyMC42LCAyMi4zLCAyMy45LCAyNS40KQpkX2ZyYW1lIDwtIGRhdGEuZnJhbWUoeD1yYW5kb21fc2FtcGxlKQpwbG90IDwtIGdncGxvdChkYXRhPWRfZnJhbWUsIGFlcyh4ID0geCkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9IGFmdGVyX3N0YXQoZGVuc2l0eSkpLGJpbnM9MykKZ2dwbG90bHkocGxvdCkKYGBgCgoKYikgV3JpdGUgYW4gUiBmdW5jdGlvbiB0aGF0IGNvbXB1dGVzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlcyB1c2luZyBhIEdhdXNzaWFuIGtlcm5lbCB3aXRoICRoPTIkLiBWYWxpZGF0ZSB5b3VyIGltcGxlbWVudGF0aW9uIGFnYWluc3QgUidzIGJ1aWx0LWluIGRlbnNpdHkoKSBmdW5jdGlvbi4KCiQkClxoYXR7Zn1faCh0KSA9IFxmcmFjezF9e25ofVxzdW1fe2k9MX1ebiBLXGxlZnQoIFxmcmFje3QtdF9pfXtofVxyaWdodCksIFwgXCBcdGV4dHsgd2hlcmUgfSBcIFwgSyh1KSA9IFxmcmFjezF9e1xzcXJ0ezJccGl9fSBlXnstdV4yLzJ9LgokJApgYGB7cn0KCgprZXJuZWxfZGVuc2l0eSA8LSBmdW5jdGlvbihzYW1wbGUsIEs9ZG5vcm0sIGg9MikgewogIG4gPC0gbGVuZ3RoKHNhbXBsZSkKICBmdW5jdGlvbih0LCBoPTIpIHsKICAgIGsgPC0gSyggb3V0ZXIodCwgc2FtcGxlLCBGVU49Ii0iKSAvIGgpCiAgICBzIDwtIHJvd1N1bXMoaykKICAgIHJldHVybiAocyAvIG4gLyBoKQogIH0KfQoKI3ggPC0gcm5vcm0oMTAwKQojeCA8LSByZ2FtbWEoMTAwLCAyKQp4IDwtIGMoMTIuMywgMTQuNywgMTUuMiwgMTYuOCwgMTguMSwgMTkuNCwgMjAuNiwgMjIuMywgMjMuOSwgMjUuNCkKI2RhdCA8LSBkYXRhLmZyYW1lKHg9eCwgeT1LKHgpKQojZ2dwbG90KGRhdCkgKwojICBnZW9tX3BvaW50KGFlcyh4LCB5KSkgKwojICBnZW9tX2xpbmUoZGF0YT1kZW5zaXR5KHgpLCBhZXMoeD14LHk9eSkpCnBsb3QoZGVuc2l0eSh4LCBrZXJuZWw9ImdhdXNzaWFuIiwgYnc9MikpCm15X2dhdXNzaWFuX2tlcm5lbF9kZW5zaXR5IDwtIGtlcm5lbF9kZW5zaXR5KHgpCiNwcmludChteV9nYXVzc2lhbl9rZXJuZWxfZGVuc2l0eSgtMTA6MTA6MC4xKSkKcG9pbnRzKHg9c2VxKC0xMCwgNTAsIDAuMSksIHk9bXlfZ2F1c3NpYW5fa2VybmVsX2RlbnNpdHkoc2VxKC0xMCwgNTAsIDAuMSkpKQoKYGBgCmMpIFdyaXRlIGEgY3VzdG9tIFIgZnVuY3Rpb24gdGhhdCBjb21wdXRlcyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZXMgdXNpbmcgdGhlIEVwYW5lY2huaWtvdiBrZXJuZWwgd2l0aCAkaD0yJC4gVmFsaWRhdGUgeW91ciBpbXBsZW1lbnRhdGlvbiBieSBjb21wYXJpbmcgcmVzdWx0cyB3aXRoIFIncyBidWlsdC1pbiBkZW5zaXR5KCkgZnVuY3Rpb24gZm9yIEdhdXNzaWFuIGtlcm5lbCBlc3RpbWF0aW9uLgoKJCQKXGhhdHtmfV9oKHQpID0gXGZyYWN7MX17bmh9XHN1bV97aT0xfV5uIEtcbGVmdCggXGZyYWN7dC10X2l9e2h9XHJpZ2h0KSwgXCBcIFx0ZXh0eyB3aGVyZSB9IFwgXCBLKHUpID0gXGZyYWN7M317NH0oMSAtIHVeMikgXCBcIFx0ZXh0eyBmb3IgfSBcIFwgfHV8IFxsZSAxLgokJAoKYGBge3J9Cgp4IDwtIGMoMTIuMywgMTQuNywgMTUuMiwgMTYuOCwgMTguMSwgMTkuNCwgMjAuNiwgMjIuMywgMjMuOSwgMjUuNCkKCksgPC0gZnVuY3Rpb24odSkgewogICAgbWFzayA8LSBhYnModSkgPD0gMQogICAgcmV0dXJuICgoKDMvNCkgKiAoMSAtIHVeMikpICogbWFzaykKfQoKa2VybmVsX2RlbnNpdHkgPC0gZnVuY3Rpb24oc2FtcGxlLCBLPWRub3JtKSB7CiAgZnVuY3Rpb24odCwgaD0yKSB7CiAgICBuIDwtIGxlbmd0aChzYW1wbGUpCiAgICAjY29tcHV0ZSBhIG1hdHJpeCB3aGVyZSBlYWNoIHJvdyBhcmUgdGhlCiAgICAjdGVybXMgdG8gc3VtIGZvciBlYWNoIGVsZW1lbnQgb2YgdAogICAgayA8LSBLKG91dGVyKHQsIHNhbXBsZSwgRlVOPSItIikgLyBoKQogICAgI3N1bSBmb3IgZWFjaCB2YWx1ZSBvZiB0CiAgICBzIDwtIHJvd1N1bXMoaykKICAgICNzY2FsZSBhbmQgcmV0dXJuCiAgICByZXR1cm4gKHMgLyBoIC8gbikKICB9Cn0KCm15X2VwYW5lY2huaWtvdl9rZXJuZWxfZGVuc2l0eSA8LSBrZXJuZWxfZGVuc2l0eSh4LCBLPUspCnhfYXhpcyA8LSBzZXEoNSwgMzAsIDAuMDEpCgoKcGxvdChkZW5zaXR5KHgsIGtlcm5lbD0iZ2F1c3NpYW4iLCBidz0yKSwgbWFpbj0iR2F1c3NpYW4gYW5kIGVwYW5lY2huaWtvdiBrZXJuZWxzIikKcG9pbnRzKHg9eF9heGlzLCB5PW15X2VwYW5lY2huaWtvdl9rZXJuZWxfZGVuc2l0eSh4X2F4aXMpLCBwY2ggPSAyMCwgY2V4ID0gMC4xKQpgYGAKCgoKZCkgSG93IGRvZXMgdGhlIGNob2ljZSBvZiBrZXJuZWwgKEdhdXNzaWFuIHZzLiBFcGFuZWNobmlrb3YpIGFmZmVjdCB0aGUgZGVuc2l0eSBlc3RpbWF0ZT8gRm9yIGJvdGgga2VybmVsIGVzdGltYXRvcnMgYXBwbGllZCB0byB0aGlzIGRhdGFzZXQsIHdoYXQgaGFwcGVucyB3aGVuIHdlIHNlbGVjdCAkaD0xLjUkIHZlcnN1cyAkaD0yLjUkPwoKYGBge3J9CnggPC0gYygxMi4zLCAxNC43LCAxNS4yLCAxNi44LCAxOC4xLCAxOS40LCAyMC42LCAyMi4zLCAyMy45LCAyNS40KQpwbG90IDwtIGdncGxvdChkYXRhPWRhdGEuZnJhbWUoeD14KSwgYWVzKHg9eCkpICsKICBnZW9tX2RlbnNpdHkoYnc9MS41LCBrZXJuZWw9ImdhdXNzaWFuIiwgYWVzKGNvbG9yPSJnYXVzaWFuIGg9MS41IikpICsKICBnZW9tX2RlbnNpdHkoYnc9MS41LCBrZXJuZWw9ImVwYW5lY2huaWtvdiIsIGFlcyhjb2xvcj0iZXBhbmVjaG5pa292IGg9MS41IikpICsKICBnZW9tX2RlbnNpdHkoYnc9Mi41LCBrZXJuZWw9ImdhdXNzaWFuIiwgYWVzKGNvbG9yPSJnYXVzaWFuIGg9Mi41IikpICsKICBnZW9tX2RlbnNpdHkoYnc9Mi41LCBrZXJuZWw9ImVwYW5lY2huaWtvdiIsIGFlcyhjb2xvcj0iZXBhbmVjaG5pa292IGg9Mi41IikpICsKICBsYWJzKHRpdGxlPSJJbXBhY3Qgb2YgQmlud2lkdGggYW5kIEtlcm5lbCBvbiBEZW5zaXR5IEFwcHJveGltYXRpb24iKQoKZ2dwbG90bHkocGxvdCkKCmBgYAoKCgoK